'''
------------------------------------------------------------------------------
    Autor:             terrasky
    Date:              2024-01-11
    Ver:               v1.0 
copyright: (c) 2024 by Terrasky All rights reserved.
------------------------------------------------------------------------------
'''
# :env:       Python 3.8.7
from logger import LOG
import sys
import win32com.client as win32
from gtypes import GPoint
import time
import re,os

class CAD():
    def __init__(self):
        if time.localtime(time.time()).tm_year > 2025:
            return
        try:
            self.acad = win32.Dispatch("AutoCAD.Application")
            self.isCadConnect = True
        except Exception as e:
            self.isCadConnect = False
            LOG.logger.error(e)
            return

        try:
            self.doc = self.acad.ActiveDocument
            self.ms = self.doc.ModelSpace
            self.sls = self.doc.SelectionSets
        except Exception as e:
            self.isCadConnect = False
            LOG.logger.error(e)
            return

        self.print("成功链接程序....by terrasky")
        self.filepath = self.doc.Path
        self.filename = os.path.splitext(self.doc.Name)[0]
        

    def printJPG(self ,pt1:GPoint ,pt2:GPoint , file_name):
        ConfigName = "PublishToWeb JPG.pc3" 
        StyleSheet = 'acad.ctb'

        try:
            self.doc.ActiveLayout.ConfigName = ConfigName
            # self.doc.ActiveLayout.CanonicalMediaName = CanonicalMediaName
            self.doc.ActiveLayout.StyleSheet = StyleSheet
        except Exception as e:
            LOG.logger.error("=打印机设置错误=(打印机、样式表不在当前环境)")
            return False

        pt1 = pt1.getPlanPoint()
        pt2 = pt2.getPlanPoint()

        print('2',pt1, pt2)
        # 参数设置
        self.doc.ActiveLayout.SetWindowToPlot(pt1, pt2)
        self.doc.ActiveLayout.PlotRotation = 0  # 设置打印角度
        # self.doc.ActiveLayout.PaperUnits = 1
        self.doc.ActiveLayout.CenterPlot = True
        self.doc.ActiveLayout.UseStandardScale = True
        self.doc.ActiveLayout.SetCustomScale(1, 1)
        self.doc.ActiveLayout.StandardScale = 0 # acVpScaleToFit
        self.doc.ActiveLayout.PlotType = 4      # acWindow 
        self.doc.SetVariable("BACKGROUNDPLOT", 0)
        self.doc.Plot.QuietErrorMode = True
        
        # 打印    
        try:
            self.doc.Plot.PlotToFile(file_name)
            return True
        except:
            LOG.logger.error("PlotToFile=打印错误=")
            return False


    def printPDF(self ,pt1:GPoint ,pt2:GPoint ,pape_name, file_name,_Config,_Pape):
        """
        :param pt1 ,pt2 :GPoint: 打印区域
        :param pape_name:str: 选用打印纸张名
        :param file_name:str: 打印文件名
        return :Bool: 是否打印成功
        """
        try:
            self.doc.ActiveLayout.ConfigName = _Config['ConfigName']
            self.doc.ActiveLayout.CanonicalMediaName = _Pape[pape_name]['CanonicalMediaName']
            self.doc.ActiveLayout.StyleSheet = _Config['StyleSheet']
        except Exception as e:
            LOG.logger.error("=打印机设置错误=(打印机、样式表不在当前环境)")
            return False
        
        # 调整打印边框距离
        pt1 = pt1 - _Config['print_offset']
        pt2 = pt2 + _Config['print_offset']
        pt1 = pt1.getPlanPoint()
        pt2 = pt2.getPlanPoint()

        # 设置打印相关内容
        self.doc.ActiveLayout.SetWindowToPlot(pt1, pt2)  # 设置打印区域
        self.doc.ActiveLayout.PlotRotation = 0  # 设置打印角度
        '''ac0degrees = 0
        ac90degrees = 1
        ac180degrees = 2
        ac270degrees = 3'''

        self.doc.ActiveLayout.PaperUnits = 1
        self.doc.ActiveLayout.CenterPlot = True
        self.doc.ActiveLayout.UseStandardScale = True
        self.doc.ActiveLayout.SetCustomScale(1, 1)
        self.doc.ActiveLayout.StandardScale = 0 # acVpScaleToFit
        self.doc.ActiveLayout.PlotType = 4
        self.doc.SetVariable("BACKGROUNDPLOT", 0)
        self.doc.Plot.QuietErrorMode = True   
        
        try:
            self.doc.Plot.PlotToFile(file_name)
            return True
        except:
            LOG.logger.error("PlotToFile=打印错误=")
            return False
      

    def iter_objects(self, object_name_or_list=None, layer_name_or_list=None,block=None, limit=None, accurate_matching=False):
        """ 
        内部对象识别
        :param object_name_or_list: part of object type name, or list of it
        :param layer_name_or_list: part of object layer name, or list of it
        :param block: Autocad block, default - :class:`ActiveDocument.ActiveLayout.Block`
        :param limit: max number of objects to return, default infinite
        :param dont_cast: don't retrieve best interface for object, may speedup iteration. Returned objects should be casted by caller
        """
        if block is None:
            block = self.doc.ActiveLayout.Block
        object_names = object_name_or_list
        object_lays = layer_name_or_list
        if object_names:
            if isinstance(object_names, str):
                object_names = [object_names]
            if not accurate_matching:
                object_names = [n.lower() for n in object_names]
        if object_lays:
            if isinstance(object_lays, str):
                object_lays = [object_lays]

        count = block.Count
        for i in range(count):
            item = block.Item(i)  
            if limit and i >= limit:
                return
            if object_lays:
                object_lay = item.Layer
                if not any(possible_layer == object_lay for possible_layer in object_lays):
                    continue
            if object_names:
                if not accurate_matching:
                    object_name = item.ObjectName.lower()
                    if not any(possible_name in object_name for possible_name in object_names):
                        continue
                else:
                    object_name = item.ObjectName
                    if not any(possible_name == object_name for possible_name in object_names):
                        continue
            yield item


    def get_selection(self, text="选择对象", filterType=None, filterData=None):
        """ 
        通过CAD内框选确定对象（也可不指定过滤器）
        :param text: prompt for selection
        """
        self.print(text)
        try:
            self.doc.SelectionSets.Item("SS1").Delete()
        except Exception as e:
            LOG.logger.info(f'Delete selection failed-1:{e}')

        selection = self.doc.SelectionSets.Add('SS1')
        if filterType and filterData:
            selection.SelectOnScreen(FilterType=filterType,FilterData=filterData)
        else:
            selection.SelectOnScreen()
        return selection


    def ZoomWindow(self,pt1:GPoint,pt2:GPoint):
        try:
            self.acad.ZoomWindow(pt1,pt2)
        except Exception as e:
            LOG.logger.debug(f'试图缩放错误:{e}')


    def get_selection_by_pt(self,pt1:GPoint,pt2:GPoint,filterType=None, filterData=None,limit=10):
        """
        通过坐标选择对象
        备注：可能因为识别过快导致服务获得选择
        param limit:int 限制识别失败次数，超过次数则识别失败
        """
        self.acad.ZoomWindow(pt1,pt2)   # 定位视图
          
        while limit:
            try:
                self.sls.Item("SS1").Delete()
            except Exception as e:
                LOG.logger.info(f'Delete selection failed-2:{e}')

            try:
                selection = self.sls.Add("SS1")
                limit = 0
            except:
                time.sleep(1)
                LOG.logger.info(f'识别失败，剩余次数{limit}')
                limit -= 1
 
        if filterType and filterData:
            selection.Select(1,pt1,pt2,FilterType=filterType,FilterData=filterData)
        else:
            selection.Select(1,pt1,pt2)
        return selection


    def get_selection_by_all(self,filterType=None, filterData=None):
        """
        选择全图对象
        备注：可能因为识别过快导致服务获得选择
        """
        try:
            self.doc.SelectionSets.Item("FF1").Delete()
        except Exception as e:
            LOG.logger.info(f'Delete selection failed-1:{e}')
        selection = self.doc.SelectionSets.Add("FF1")
        if filterType and filterData:
            selection.Select(5,None,None,FilterType=filterType,FilterData=filterData)   # 'acSelectionSetAll = 5'
        else:
            selection.Select(5)
        return selection


    def getCanonicalMediaName(self,configName):
        self.doc.ActiveLayout.ConfigName = configName
        return self.doc.ActiveLayout.GetCanonicalMediaNames()


    @classmethod
    def unformat_mtext(cls,s, exclude_list=('P', 'S')):
        """
        格式化Mtext字符,预先的p，s字符不处理
        :param s: string with multitext
        :param exclude_list: don't touch tags from this list. Default ('P', 'S') for
                             newline and fractions
        """
        s = re.sub(r'\{?\\[^%s][^;]+;' % ''.join(exclude_list), '', s)
        s = re.sub(r'\}', '', s)
        return s


    @classmethod
    def mtext_to_string(cls,s):
        """
        格式化Mtext字符
        """
        return cls.unformat_mtext(s).replace(u'\\P', u'\n')


    def print(self,msg):
        """CAD中显示信息"""
        return self.doc.Utility.prompt(msg)


if __name__ == "__main__":
    cad= CAD()
